﻿//-------------------------------------------------------------------
//	Copyright (c) 2010-2014  Zhirnov Andrey
//	This file is part of the "UX Standard Lib" project.
//	See copyright notice in "UX_STL_Core.h".
//-------------------------------------------------------------------

#ifndef _UXS_STATIC_ARRAY_H
#define _UXS_STATIC_ARRAY_H

#pragma once

#include "TArray.h"

namespace UX_STL
{
namespace UXTypes
{

	
	#define RET_ERROR( _ret )	{ WARNING("error in static array");  return _ret; }
	#define RET_FALSE			RET_ERROR( false )
	#define RET_VOID			RET_ERROR( ; )



	//
	// Static Array
	//

	#define TStaticArray		static_array

	template <typename T, usize C, typename S = TCopyStrategy<T> >
	struct TStaticArray
	{
		STATIC_ASSERT( C > 0, "invalid array size" );

	// types
	public:
		typedef TStaticArray<T,C,S>		Self;
		typedef S						Strategy;
		typedef T						value_t;
		typedef uni_array<T>			uni_arr;
		typedef uni_array<const T>		uni_c_arr;
		typedef	T *						iterator;
		typedef const T *				const_iterator;
		
		static const usize	STATIC_COUNT	= C;
		static const usize	STATIC_SIZE		= C * sizeof(T);


	// variables
	protected:
		T	_pMemory[C];


	// methods
	private:
		void _Create (const T *pMem, usize uCount);

	public:
		TStaticArray ();

		TStaticArray (const Self &sArray);

		explicit
		TStaticArray (const T& tValue);

		explicit
		TStaticArray (const uni_c_arr &sArray);

		TStaticArray (const T *pMem, usize uCount);

		T		*	ptr ()								{ return _pMemory; }
		const T	*	ptr () const						{ return _pMemory; }
		
		T		&	Back ()								{ return _pMemory[Count()-1]; }
		const T	&	Back () const						{ return _pMemory[Count()-1]; }
		T		&	Front ()							{ return _pMemory[0]; }
		const T	&	Front () const						{ return _pMemory[0]; }

		bool	operator !  () const					{ return not Empty(); }
		
		bool	operator == (const uni_c_arr &r) const	{ return uni_c_arr(*this) == r; }
		bool	operator != (const uni_c_arr &r) const	{ return not ( *this == r ); }

		operator		uni_arr ()						{ return uni_arr( _pMemory, Count() ); }
		operator const	uni_c_arr () const				{ return uni_c_arr( _pMemory, Count() ); }

		T		&	operator [] (usize i);
		const T	&	operator [] (usize i) const;

		
		bool At (usize uNumb, OUT T & tValue) const;

		void Clear ();												// освобождает память
		void ClearMem ();											// обнуляет память
		
		void Copy (const uni_c_arr &sArray);						// копировать массив

		bool Set (usize uNumb, const T &tValue);
		RVALUE_REFERENCE_ONLY( bool Set (usize i, T &&tValue); )

		void Swap (usize uNumb1, usize uNumb2);						// меняет местами элементы
		
		template <typename T2, typename S2, typename A2>
		void ToDynamic (array<T2,S2,A2> &sArr) const;				// конвертировать данные массива
		
		usize GetIndex (const T &elem) const	{ return uni_c_arr(*this).GetIndex( elem ); }

		bool  Empty ()		const	{ return false; }				// пустой?
		usize Count ()		const	{ return C; }					// количество элементов
		usize Capacity ()	const	{ return C; }					// выделено памяти под элементы
		usize Size ()		const	{ return Count()*sizeof(T); }	// размер видимой памяти
		usize FullSize ()	const	{ return Capacity()*sizeof(T); }// полный размер выделенной памяти
		usize LastIndex ()	const	{ return Count()-1; }			// индекс последнего элемента
	
		uni_arr		SubArray (usize uPos, usize uCount)				{ return uni_arr(*this).SubArray( uPos, uCount ); }
		uni_c_arr	SubArray (usize uPos, usize uCount)	const		{ return uni_c_arr(*this).SubArray( uPos, uCount ); }


		iterator		Begin ()			{ return _pMemory; }	// возвращает первый элемент
		const_iterator	Begin () const		{ return _pMemory; }

		iterator		End ()				{ return _pMemory + Count(); }	// возвращает указатель на конец массива
		const_iterator	End () const		{ return _pMemory + Count(); }

		iterator		GetIter (usize uNumb);						// возвращает элемент
		const_iterator	GetIter (usize uNumb) const;

		bool IsBegin (const_iterator pIter)	const;					// первый?
		bool IsEnd (const_iterator pIter)	const;					// последний?
	};


	
	template <typename T, usize C, typename S>
	inline TStaticArray<T,C,S>::TStaticArray ()
	{
		Strategy::Create( _pMemory, Count() );
	}

		
	template <typename T, usize C, typename S>
	inline TStaticArray<T,C,S>::TStaticArray (const T *pMem, usize uCount)
	{
		Copy( uni_c_arr( pMem, uCount ) );
	}

		
	template <typename T, usize C, typename S>
	inline TStaticArray<T,C,S>::TStaticArray (const TStaticArray<T,C,S> &sArray)
	{
		Strategy::Copy( _pMemory, sArray._pMemory, Count() );
	}
	
		
	template <typename T, usize C, typename S>
	inline TStaticArray<T,C,S>::TStaticArray (const uni_c_arr &sArray)
	{
		Copy( sArray );
	}
	
	
	template <typename T, usize C, typename S>
	inline void TStaticArray<T,C,S>::Copy (const uni_c_arr &sArray)
	{
		//ASSERT( sArray.Count() == Count() and "size mismatch" );

		if ( sArray.Empty() )
		{
			Clear();
			return;
		}

		if ( sArray.ptr() == ptr() ) {
			WARNING( "" );
			return;
		}

		if ( sArray.Count() > Count() )
		{
			WARNING( "source array is too big!" );
			Strategy::Copy( _pMemory, sArray.ptr(), Count() );
		}
		else
		{
			Strategy::Copy( _pMemory, sArray.ptr(), sArray.Count());
			Strategy::Create( _pMemory + sArray.Count(), Count() - sArray.Count() );
		}
	}

		
	template <typename T, usize C, typename S>
	inline TStaticArray<T,C,S>::TStaticArray (const T& tValue)
	{
		for (usize i = 0; i < Count(); ++i) {
			Strategy::Construct( _pMemory + i, tValue );
		}
	}
		
	template <typename T, usize C, typename S>
	inline T & TStaticArray<T,C,S>::operator [] (usize uNumb)
	{
		ASSUME( uNumb < Count() );
		return _pMemory[ uNumb ];
	}

		
	template <typename T, usize C, typename S>
	inline const T & TStaticArray<T,C,S>::operator [] (usize uNumb) const
	{
		ASSUME( uNumb < Count() );
		return _pMemory[ uNumb ];
	}
	
	
	template <typename T, usize C, typename S>
	inline bool TStaticArray<T,C,S>::At (usize uNumb, T & tValue) const
	{
		if ( uNumb >= Count() )  return false;
		Strategy::Construct( &tValue, _pMemory[uNumb] );
		return true;
	}

	
	template <typename T, usize C, typename S>
	inline bool TStaticArray<T,C,S>::Set (usize uNumb, const T &tValue)
	{
		if ( uNumb >= Count() ) return false;
		Strategy::Construct( _pMemory + uNumb, tValue );
		return true;
	}
	

	RVALUE_REFERENCE_ONLY(
		template <typename T, usize C, typename S>
		inline bool TStaticArray<T,C,S>::Set (usize uNumb, T&& tValue)
		{
			if ( uNumb >= Count() ) return false;
			Strategy::Construct( _pMemory + uNumb, ToRValueRef( tValue ) );
			return true;
		}
	)

		
	template <typename T, usize C, typename S>
	inline void TStaticArray<T,C,S>::Clear ()
	{
		ClearMem();
	}

		
	template <typename T, usize C, typename S>
	inline void TStaticArray<T,C,S>::ClearMem ()
	{
		Strategy::Destroy( _pMemory, Count() );
		Strategy::Create(  _pMemory, Count() );
	}

		
	template <typename T, usize C, typename S>
	inline void TStaticArray<T,C,S>::Swap (usize uNumb1, usize uNumb2)
	{
		if ( uNumb1 >= Count() or uNumb2 >= Count() )
			RET_VOID;

		T	temp;
		Strategy::Replace( &temp,			  _pMemory[uNumb2] );
		Strategy::Replace( _pMemory + uNumb2, _pMemory[uNumb1] );
		Strategy::Replace( _pMemory + uNumb1, temp );
	}
		
		
	template <typename T, usize C, typename S>
	template <typename T2, typename S2, typename A2>
	inline void TStaticArray<T,C,S>::ToDynamic (array<T2,S2,A2> &sArr) const
	{
		sArr.ClearMem();
		sArr.Reserve( Count() );

		for (usize i = 0; i < Count(); ++i)
			sArr.PushBack( T2( _pMemory[i] ) );
	}

		
	template <typename T, usize C, typename S>
	inline T * TStaticArray<T,C,S>::GetIter (usize uNumb)
	{
		ASSERT( uNumb < Count() );
		return _pMemory + uNumb;
	}

		
	template <typename T, usize C, typename S>
	inline const T * TStaticArray<T,C,S>::GetIter (usize uNumb) const
	{
		ASSERT( uNumb < Count() );
		return _pMemory + uNumb;
	}

		
	template <typename T, usize C, typename S>
	inline bool TStaticArray<T,C,S>::IsBegin (const_iterator pIter) const
	{
		return pIter == Begin();
	}


	template <typename T, usize C, typename S>
	inline bool TStaticArray<T,C,S>::IsEnd (const_iterator pIter) const
	{
		return pIter == End();
	}


	#undef TStaticArray
	#undef RET_ERROR
	#undef RET_FALSE
	#undef RET_VOID



	// Make Array Function Helpers
#	define _MAKE_ARRAY( _count, _params, _args ) \
		template <typename T> \
		static inline static_array<T,_count> Create (_params) \
		{ \
			const T					tmp[] = { _args }; \
			STATIC_ASSERTE( COUNT_OF(tmp) == _count ); \
			static_array<T,_count>	res( tmp ); \
			return res; \
		}

	struct StaticArrayBuilder
	{
		_MAKE_ARRAY(  1,
					ARGS( T a00 ),
					ARGS( a00 )
		)
		_MAKE_ARRAY(  2,
					ARGS( T a00, T a01 ),
					ARGS( a00, a01 )
		)
		_MAKE_ARRAY(  3,
					ARGS( T a00, T a01, T a02 ),
					ARGS( a00, a01, a02 )
		)
		_MAKE_ARRAY(  4,
					ARGS( T a00, T a01, T a02, T a03 ),
					ARGS( a00, a01, a02, a03 )
		)
		_MAKE_ARRAY(  5,
					ARGS( T a00, T a01, T a02, T a03, T a04 ),
					ARGS( a00, a01, a02, a03, a04 )
		)
		_MAKE_ARRAY(  6,
					ARGS( T a00, T a01, T a02, T a03, T a04, T a05 ),
					ARGS( a00, a01, a02, a03, a04, a05 )
		)
		_MAKE_ARRAY(  7,
					ARGS( T a00, T a01, T a02, T a03, T a04, T a05, T a06 ),
					ARGS( a00, a01, a02, a03, a04, a05, a06 )
		)
		_MAKE_ARRAY(  8,
					ARGS( T a00, T a01, T a02, T a03, T a04, T a05, T a06, T a07 ),
					ARGS( a00, a01, a02, a03, a04, a05, a06, a07 )
		)
		_MAKE_ARRAY(  9,
					ARGS( T a00, T a01, T a02, T a03, T a04, T a05, T a06, T a07, T a08 ),
					ARGS( a00, a01, a02, a03, a04, a05, a06, a07, a08 )
		)
		_MAKE_ARRAY( 10,
					ARGS( T a00, T a01, T a02, T a03, T a04, T a05, T a06, T a07, T a08, T a09 ),
					ARGS( a00, a01, a02, a03, a04, a05, a06, a07, a08, a09 )
		)
		_MAKE_ARRAY( 11,
					ARGS( T a00, T a01, T a02, T a03, T a04, T a05, T a06, T a07, T a08, T a09, T a10 ),
					ARGS( a00, a01, a02, a03, a04, a05, a06, a07, a08, a09, a10 )
		)
		_MAKE_ARRAY( 12,
					ARGS( T a00, T a01, T a02, T a03, T a04, T a05, T a06, T a07, T a08, T a09, T a10, T a11 ),
					ARGS( a00, a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11 )
		)
		_MAKE_ARRAY( 13,
					ARGS( T a00, T a01, T a02, T a03, T a04, T a05, T a06, T a07, T a08, T a09, T a10, T a11, T a12 ),
					ARGS( a00, a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11, a12 )
		)
		_MAKE_ARRAY( 14,
					ARGS( T a00, T a01, T a02, T a03, T a04, T a05, T a06, T a07, T a08, T a09, T a10, T a11, T a12, T a13 ),
					ARGS( a00, a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11, a12, a13 )
		)
		_MAKE_ARRAY( 15,
					ARGS( T a00, T a01, T a02, T a03, T a04, T a05, T a06, T a07, T a08, T a09, T a10, T a11, T a12, T a13, T a14 ),
					ARGS( a00, a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11, a12, a13, a14 )
		)
	};

#	undef _MAKE_ARRAY

//-------------------------------------------------------------------

}	// UXTypes
}	// UX_STL

#endif	// _UXS_STATIC_ARRAY_H